home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / SDKs / PCI Driver Development Kit / • Tools / Utility / LogLibrary 950622 / Src / LogLibrary.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-20  |  27.9 KB  |  937 lines  |  [TEXT/MPCC]

  1. /*                                    LogLibrary.c                                    */
  2. /*
  3.  * LogLibrary.c
  4.  * Copyright © 1992-95 Apple Computer Inc. All Rights Reserved.
  5.  * Programmed by Martin Minow,
  6.  *    Internet:    minow@apple.com
  7.  *    AppleLink:    MINOW
  8.  *
  9.  * Edit History
  10.  * This library implements a drop-in logging capability for device drivers,
  11.  * callback functions, applications, and all other Power Macintosh code segments
  12.  * running under a version of system software that supports the System Name Registry
  13.  * and Driver Services libraries defined in Designing PCI Cards and Drivers for
  14.  * Power Macintosh Computers.
  15.  *
  16.  * The overall philosophy is that an application or driver allocates a record in
  17.  * system resident memory that contains a small preamble and one or more short blocks
  18.  * of data. The driver logs data to the log area, where each log record contains an
  19.  * identification value, a timestamp and up to eight longwords of data. The data
  20.  * entry is self-formatting, so a generic log display application may be written.
  21.  *
  22.  * To permit logging from primary interupt level (which precludes using Software
  23.  * Interrupts), the log record common area is protected by a critical section
  24.  * (test and set) variable. The log is "lossy" in that failure to enter the
  25.  * critical section will lose data, but will not stall or block the caller.
  26.  *
  27.  * This function will crash (or fail to load) if the DriverServices library
  28.  * is unavailable.
  29.  *
  30.  * LogLibrary.c may be linked into a PCI Device Driver: it does not call
  31.  * any Macintosh Toolbox functions. The global symbol _LogLibraryGlobal
  32.  * (Note the leading underscore) must be exported globally-shared.
  33.  *
  34.  * LogLibrary is intended for use by PowerPC native applications and other
  35.  * program entities. A 68000 version, in LogLibrary68.c, can be used by
  36.  * emulated applications and other code segments, such as MacsBug dcmd's.
  37.  *
  38.  * Usage:
  39.  *    PowerPC (native):
  40.  *        Include "LogLibrary.h" and LogLibrary.c in all compilations.
  41.  *    68000 emulation
  42.  *        Include "LogLibrary.h" in all compilations. The first GetLogRecordPtr
  43.  *        call must be able to call the Code Fragment Manager -- which means that
  44.  *        it must be able to open files and allocate memory. You can call it
  45.  *        with a dummy argument "foo = GetLogRecordPtr("\pFoo");"
  46.  *
  47.  * Edit History
  48.  * 1995.03.28    MM    CompareAndSwap screw-up.
  49.  * 1995.03.31    MM    Removed UpTime return from CopyLogEntry (only the dcmd needs it)
  50.  *                    Added LogLibraryUpTime to return UpTime in nanoseconds for 68000
  51.  *                    functions.
  52.  *                    ResolveLogLibraryLinkage is now a static function: it was global.
  53.  * 1995.04.07    MM    Some compilers mis-handle functions returning structures. Store
  54.  *                    UpTime returned values in a volatile local variable before using
  55.  *                    them in a function call.
  56.  * 1995.04.11    MM    Ensure that the Code Fragment is loaded into the System Heap.
  57.  * 1995.04.29    MM    Split into LogLibrary (PPC) and LogLibrary68 (68000). This
  58.  *                    was done for the benefit of the MacsBug dcmd. This change
  59.  *                    resulted in a significant simplification of the code as all
  60.  *                    of the cross-architecture code was moved to LogLibrary68.c.
  61.  * 1995.05.25    MM    Some minor cleanups (parameters marked "const"). Removed all
  62.  *                    dependencies on the Name Registry. This is not trivial, but
  63.  *                    removed a lot of code (and simplified the library).
  64.  * 1995.05.26    MM    Folded the 68000 code back into the PowerPC code; this means
  65.  *                    that 68000 modules can do everything except create a LogRecord.
  66.  *                    Some of the ugliness is hidden in macros.
  67.  */
  68.  
  69. #include "LogLibrary.h"
  70. #define LOG    (*logRecordPtr)
  71.  
  72. #ifndef TRUE
  73. #define TRUE    1
  74. #define FALSE    0
  75. #endif
  76. #define EOS        '\0'
  77.  
  78. #if GENERATINGPOWERPC
  79. /*
  80.  * This is the only global in the native LogLibrary. It must be exported "shared global."
  81.  * If you change the name, be sure to change the argument to FindSymbol below.
  82.  */
  83. LogRecordPtr                _LogLibraryGlobal;
  84. #define GetFirstLogRecord()    (_LogLibraryGlobal)
  85. static OSStatus             AddNewLogRecord(        /* SecondaryInterruptHandler2    */
  86.         void                    *p1,
  87.         void                    *p2
  88.     );
  89. #else
  90. #include <GestaltEqu.h>
  91. /*
  92.  * 68000 only.
  93.  */
  94. unsigned short                __DisableInterrupts(void) = {
  95.         0x40C0,                                    /* move        sr,d0                    */
  96.         0x46FC, 0x2600                            /* move        #0x2600,sr                */
  97.     };
  98. void                        __EnableInterrupts(unsigned long saveSR) = {
  99.         0x46EF,    0x0002                            /* move        2(sp),sr                */
  100.     };
  101. /*
  102.  * When compiled for 68000's, the first call to GetLogRecordPtr initalizes this so
  103.  * GetLogRecordPtr can access the LogRecord linked list.
  104.  */ 
  105. static LogRecordPtr            GetFirstLogRecord(void);
  106.  
  107. #endif
  108.  
  109. static Boolean32            EnableLogCapability(
  110.         LogRecordPtr            logRecordPtr,        /* This log record                */
  111.         Boolean32                callerRequest,        /* TRUE to enable something        */
  112.         UInt32                    enableMaskBit        /* Selector bitmask                */
  113.     );
  114. static Boolean                StringEquals(
  115.         const char                *str1,
  116.         const char                *str2
  117.     );
  118.  
  119. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  120.  * MakeLogRecord
  121.  *
  122.  * The first time this function is called after the system is started, it creates a
  123.  * log record for this name. The log is initially enabled, and deletes overflow at the
  124.  * start. It thus retains the *end* of the log -- this is useful for ongoing logging,
  125.  * but may lose the start of a disaster sequence (as it records the flow of errors
  126.  * through the system, rather than the intial cause.
  127.  *
  128.  * Returns the LogRecordPtr for this log, or NULL if unsuccessful.
  129.  *
  130.  * Only the "native" compilation can create LogRecords. When compiled for 68000's,
  131.  * MakeLogRecord is identical to GetLogRecordPtr. This is needed to minimize
  132.  * cross-language confusion.
  133.  */
  134. pascal LogRecordPtr
  135. MakeLogRecord(
  136.         const LogRecordNamePtr    logRecordNamePtr,        /* Log name (C-string)        */
  137.         UInt32                    nEntries                /* Number of Log entries    */
  138.     )
  139. {
  140.         LogRecordPtr            logRecordPtr;            /* This log record            */
  141. #if GENERATINGPOWERPC
  142.         UInt32                    logRecordSize;
  143.         OSStatus                osStatus;
  144. #endif
  145.  
  146.         logRecordPtr = GetLogRecordPtr(logRecordNamePtr);
  147. #if GENERATINGPOWERPC
  148.         if (logRecordPtr == NULL) {
  149.             /*
  150.              * The log record is not in our linked list.
  151.              *
  152.              * Hand-compute the LogEntryRecord size to make sure that compiler quirks
  153.              * don't mess us up. We check that kSizeofLogEntry equals sizeof
  154.              * (LogEntryRecord). The ANSI C Standard does not permit "sizeof" in a
  155.              * #define statement, so this check must be made at runtime.
  156.              *
  157.              * Note: do not replace the hard-coded numbers by sizeof statements:
  158.              * this strange sequence ensures that the library can be complied
  159.              * by several compilers.
  160.              */
  161.             #define kSizeofLogRecordEntry                                    \
  162.                     ( 8                            /* sizeof eventTime        */    \
  163.                     + 4                            /* sizeof sequence        */    \
  164.                     + 4                            /* sizeof idCode        */    \
  165.                     + 4                            /* sizeof format        */    \
  166.                     + (4 * kLogEntryDataSize)    /* sizeof data[]        */    \
  167.                     )
  168.             if (sizeof (LogEntryRecord) == kSizeofLogRecordEntry    /* Size ok?        */
  169.              && nEntries > 0) {                                        /* Has entries?    */
  170.                 /*
  171.                  * We must create a new log record. The actual record has one extra
  172.                  * entry to prevent the "full vs. empty" bug.
  173.                  */
  174.                 logRecordSize = sizeof (LogRecord)
  175.                             + (nEntries * sizeof (LogEntryRecord));
  176.                 logRecordPtr = (LogRecordPtr)
  177.                             PoolAllocateResident(logRecordSize, TRUE);
  178.                 if (logRecordPtr != NULL) {
  179.                     /*
  180.                      * Initialize the LogRecord
  181.                      */
  182.                     LOG.entryMaxIndex = nEntries + 1;
  183.                     LOG.sequenceCounter = 1;
  184.                     CStrNCopy(
  185.                         LOG.logName,
  186.                         (char *) logRecordNamePtr,
  187.                         sizeof LOG.logName - 1
  188.                     );
  189.                     LOG.flags = ( (1 * kLogDataEnabledMask)        /* Enabled            */
  190.                             | (0 * kLogDataPreserveFirstMask)    /* Save last        */
  191.                             | (1 * kLogDataRecordUpTimeMask)    /* Record UpTime    */
  192.                             | (0 * kLogDataWrapAroundMask)        /* Always zero        */
  193.                             );
  194.                     osStatus = CallSecondaryInterruptHandler2(
  195.                                 AddNewLogRecord,
  196.                                 NULL,
  197.                                 logRecordPtr,
  198.                                 NULL
  199.                             );
  200.                     if (osStatus != noErr) {
  201.                         PoolDeallocate((LogicalAddress) logRecordPtr);
  202.                         /*
  203.                          * The only (expected) error indicates asynchronous creation.
  204.                          * We should be able to get the LogRecord now.
  205.                          */
  206.                         logRecordPtr = GetLogRecordPtr(logRecordNamePtr);
  207.                     }
  208.                 }
  209.             }
  210.         }
  211. #else /* 68000 only */
  212.         do {
  213.             nEntries = nEntries;                            /* Unused for 68000                */
  214.         } while (0);
  215. #endif /* GENERATINGPOWERPC */
  216.         return (logRecordPtr);
  217. }
  218.  
  219. #if GENERATINGPOWERPC
  220. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  221.  * AddNewLogRecord
  222.  *
  223.  * This is the only function to add a LogRecord. It is a Secondary Interrupt routine so
  224.  * that only one caller can add a record.
  225.  */
  226. static OSStatus
  227. AddNewLogRecord(
  228.         void                    *p1,
  229.         void                    *p2
  230.     )
  231. {
  232.         OSStatus                osStatus;
  233.         LogRecordPtr            logRecordPtr;
  234.         Boolean                    updated;
  235.  
  236.         (p2 = p2);                                        /* Parameter p2 is unused    */
  237.         logRecordPtr = (LogRecordPtr) p1;
  238.         /*
  239.          * If several tasks try to add the same record, there is a tiny chance that
  240.          * this function can be entered with the record already added, even though
  241.          * MakeLogRecord checked for this. Thus, we check here (since Secondary
  242.          * Interrupts are not re-entrant). If the record is now present, this function
  243.          * returns an error status and MakeLogRecord disposes of the newly-created
  244.          * (and now redundant) LogRecord and then re-calls GetLogRecordPtr to fetch
  245.          * the correct LogRecordPtr.
  246.          */
  247.         if (GetLogRecordPtr(LOG.logName) != NULL)
  248.             osStatus = fBsyErr;
  249.         else {
  250.             do {
  251.                 LOG.logRecordLink = _LogLibraryGlobal;
  252.                 updated = CompareAndSwap(            /* Is CompareAndSwap needed?    */
  253.                             (UInt32) LOG.logRecordLink,
  254.                             (UInt32) logRecordPtr,
  255.                             (UInt32 *) &_LogLibraryGlobal
  256.                         );
  257.             } while (updated == FALSE);
  258.             osStatus = noErr;
  259.         }
  260.         return (osStatus);
  261. }
  262. #endif /* GENERATINGPOWERPC */
  263.  
  264.  
  265. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  266.  * GetLogRecordPtr
  267.  *
  268.  * Find the address of the LogRecord.
  269.  */
  270. pascal LogRecordPtr
  271. GetLogRecordPtr(
  272.         const LogRecordNamePtr    logRecordNamePtr        /* Log name (C-string)        */
  273.     )
  274. {
  275.         LogRecordPtr            logRecordPtr;
  276.  
  277.         logRecordPtr = GetFirstLogRecord();
  278.         for (; logRecordPtr != NULL; logRecordPtr = LOG.logRecordLink) {
  279.             if (StringEquals((const char *) logRecordNamePtr, LOG.logName))
  280.                 break;
  281.         }
  282.         return (logRecordPtr);
  283. }
  284.  
  285. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  286.  * CopyLogRecordInfo
  287.  *
  288.  * This function copies the permanent part of the LogRecord (srcLogRecordPtr) to a
  289.  * caller-designated area (dstLogRecordPtr). It is only for the benefit of the DCMD.
  290.  * It returns noErr if successful, paramErr if either the source or destination parameters
  291.  * are incorrect, or a Shared Library Manager error. This function ignores the interlock
  292.  * semaphore. Thus, it may return inconstent data. It does not copy the first LogRecordEntry.
  293.  *
  294.  * Only the DCMD is permitted to call this function!
  295.  */
  296. pascal OSErr
  297. CopyLogRecordInfo(
  298.         const LogRecordPtr        srcLogRecordPtr,
  299.         LogRecordPtr            dstLogRecordPtr
  300.     )
  301. {
  302.         OSErr                    status;
  303.  
  304.         if (srcLogRecordPtr == NULL || dstLogRecordPtr == NULL)
  305.             status = paramErr;
  306.         else {
  307. #if GENERATINGPOWERPC
  308.             BlockCopy(            /* Driver Suppor Library only        */
  309. #else
  310.             BlockMoveData(        /* Macintosh Toolbox only            */
  311. #endif
  312.                 srcLogRecordPtr,
  313.                 dstLogRecordPtr,
  314.                 sizeof (LogRecord) - sizeof (LogEntryRecord)
  315.             );
  316.             status = noErr;
  317.         }
  318.         return (status);
  319. }
  320.  
  321. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  322.  * (LogString)
  323.  *
  324.  * This is a function version of the LogEntryString macro.
  325.  */
  326. pascal OSErr
  327. (LogString)(
  328.         LogRecordPtr            logRecordPtr,            /* This log record            */
  329.         OSType                    idCode,                    /* Entry identifier            */
  330.         ConstStr255Param        string                    /* The datum                */
  331.     )
  332. {
  333.         OSErr                    status;
  334.  
  335.         status = WriteLogEntry(logRecordPtr, idCode, LogStringFormat, string);
  336.         return (status);
  337. }
  338.  
  339. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  340.  * (LogStatusString)
  341.  *
  342.  * This is a function version of the LogDataStatusString macro.
  343.  */
  344. pascal OSErr
  345. (LogStatusString)(
  346.         LogRecordPtr            logRecordPtr,            /* This log record            */
  347.         OSType                    idCode,                    /* Entry identifier            */
  348.         OSErr                    callerStatus,            /* Status error code        */
  349.         ConstStr255Param        string                    /* The datum                */
  350.     )
  351. {
  352.         OSErr                    status;
  353.  
  354.         status = WriteLogEntry(
  355.                 logRecordPtr,
  356.                 idCode,
  357.                 LogStatusFormat,
  358.                 (signed long) callerStatus,
  359.                 string
  360.             );
  361.         return (status);
  362. }
  363.  
  364. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  365.  * WriteLogEntry
  366.  *
  367.  * This function logs data if logging is enabled.
  368.  *        idCode        A user-controlled value, by convention an OSType (4-byte
  369.  *                    character), that identifies this entry. The display program
  370.  *                    prints it.
  371.  *        format        The format of the remaining data: kLogFormatString if the
  372.  *                    datum is a pascal string, otherwise, it is as created by the
  373.  *                    LogFormat macro.
  374.  *        ...            Additional data as needed. These values must be cast to
  375.  *                    longwords to prevent ThinkC/MPW incompatibilities.
  376.  * Note: The MixedMode Manager does not support variable-length argument lists.
  377.  * Hence, this function is always in the "current" architecture.
  378.  */
  379. OSErr
  380. WriteLogEntry(
  381.         LogRecordPtr            logRecordPtr,            /* This log record            */
  382.         OSType                    idCode,
  383.         UInt32                    format,
  384.         ...
  385.     )
  386. {
  387.         va_list                    argPtr;
  388.         register UInt32            *destPtr;
  389.         LogEntryRecord            theEntry;
  390.         UInt16                    maxString;
  391.         register UInt16            stringLength;
  392.         register UInt16            i;
  393.         UInt16                    thisFormat;
  394.         StringPtr                theString;
  395.         OSErr                    status;
  396. #define ENTRY    (theEntry)
  397.  
  398.         if (logRecordPtr == NULL || (LOG.flags & kLogDataEnabledMask) == 0)
  399.             status = noErr;
  400.         else {
  401.             ENTRY.idCode = idCode;
  402.             ENTRY.format = format;
  403.             va_start(argPtr, format);
  404.             destPtr = ENTRY.data;
  405.             maxString = sizeof (ENTRY.data);
  406.             while (maxString > 0) {
  407.                 thisFormat = format & kLogFormatMask;
  408.                 switch (thisFormat) {
  409.                 case kLogFormatEnd:
  410.                     goto exitLoop;
  411.                 case kLogFormatString:
  412.                     theString = va_arg(argPtr, StringPtr);
  413.                     if (theString == NULL)
  414.                         theString = "\p{null}";
  415.                     stringLength = theString[0];
  416.                     if (stringLength >= maxString)
  417.                         stringLength = maxString - 1;
  418.                     for (i = 1; i <= stringLength; i++)
  419.                         ((Ptr) destPtr)[i] = theString[i];
  420.                     ((Ptr) destPtr)[0] = stringLength;
  421.                     goto exitLoop;
  422.                 default:                /* Signed or unsigned long value            */
  423.                     *destPtr++ = va_arg(argPtr, unsigned long);
  424.                     format >>= kLogFormatShift;
  425.                     maxString -= sizeof (unsigned long);
  426.                     break;
  427.                 }
  428.             }
  429. exitLoop:    va_end(nextArg);
  430.             status = StoreLogEntry(logRecordPtr, &theEntry);
  431.         } /* If the LogData record exists */
  432.         return (status);
  433. #undef ENTRY
  434. }
  435.  
  436. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  437.  * StoreLogEntry
  438.  *
  439.  * Store a formatted LogEntryRecord in the log. This is the only routine that
  440.  * writes the log entry. Normally, it is only called by WriteLogData.
  441.  */
  442. pascal OSErr
  443. StoreLogEntry(
  444.         LogRecordPtr            logRecordPtr,            /* This log record            */
  445.         const LogEntryPtr        logEntryPtr                /* Gets this entry            */
  446.     )
  447. {
  448.         OSErr                    status;
  449.  
  450.         UInt32                    putIndex;
  451.         UInt32                    getIndex;
  452. #define ENTRY    (*logEntryPtr)
  453.  
  454.         if (logRecordPtr == NULL || (LOG.flags & kLogDataEnabledMask) == 0)
  455.             status = noErr;
  456.         else {
  457.             if ((LOG.flags & kLogDataRecordUpTimeMask) == 0)
  458.                 ENTRY.eventTime.hi = ENTRY.eventTime.lo = 0;
  459.             else {
  460.                 ENTRY.eventTime = UpTime();
  461.             }
  462. #if GENERATINGPOWERPC
  463.             ENTRY.sequence = IncrementAtomic((SInt32 *) &LOG.sequenceCounter);
  464.             if (CompareAndSwap(0, 1, &LOG.semaphore) == FALSE) { /* Grab semaphore    */
  465.                 IncrementAtomic((SInt32 *) &LOG.lostLockCounter); /* Oops, it's busy*/
  466.                 status = fBsyErr;                                /* Return "busy"    */
  467.             }
  468.             else {                                                /* Nope, we got it    */
  469. #else
  470.             {
  471.                 short            oldSR = __DisableInterrupts();
  472.  
  473.                 ENTRY.sequence = LOG.sequenceCounter++;
  474. #endif
  475.                 /*
  476.                  * The ring buffer is designed so that put == get implies empty
  477.                  * and pointers are always incremented before use.
  478.                  */
  479.                 putIndex = LOG.entryPutIndex + 1;
  480.                 if (putIndex >= LOG.entryMaxIndex) {
  481.                     putIndex = 0;
  482.                     LOG.flags |= kLogDataWrapAroundMask;
  483.                 }
  484.                 if (putIndex == LOG.entryGetIndex) {        /* Did it fill?        */
  485.                     if ((LOG.flags & kLogDataPreserveFirstMask) != 0)
  486.                         status = writErr;                    /* Keeping first    */
  487.                     else {
  488.                         /*
  489.                          * We want to retain the latest entry. Do this by
  490.                          * advancing the "get" pointer as if the earliest entry
  491.                          * has been read. Then jump around the if bracket to store
  492.                          * this datum.
  493.                          */
  494.                         getIndex = LOG.entryGetIndex + 1;
  495.                         if (getIndex >= LOG.entryMaxIndex)
  496.                             getIndex = 0;
  497.                         LOG.entryGetIndex = getIndex;
  498.                         goto storeDatum;
  499.                     }
  500.                 }
  501.                 else {
  502. storeDatum:            LOG.entries[putIndex] = ENTRY;
  503.                     LOG.entryPutIndex = putIndex;
  504.                     status = noErr;                                /* Data stored ok    */
  505.                 }
  506. #if GENERATINGPOWERPC
  507.                 LOG.semaphore = 0;                                /* Free semaphore    */
  508. #else
  509.                 __EnableInterrupts(oldSR);
  510. #endif
  511.             }
  512.         }
  513.         return (status);
  514. }
  515.  
  516. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  517.  * ReadLogEntry
  518.  *
  519.  * Read the next LogRecord entry. This returns a copy of the entry, if one is
  520.  * available. The following status may be returned:
  521.  *    noErr        Valid data was returned.
  522.  *    readErr        No data is available.
  523.  *    fBsyErr        The semaphore was locked by another function (presumable writing
  524.  *                into the log). Try again later.
  525.  *    other        Code Fragment error.
  526.  */
  527. pascal OSErr
  528. ReadLogEntry(
  529.         LogRecordPtr            logRecordPtr,            /* This log record            */
  530.         LogEntryPtr                thisLogEntry            /* Store entry here            */
  531.     )
  532. {
  533.         OSErr                    status;
  534.         UInt32                    getIndex;
  535.  
  536.         status = readErr;                                    /* Presume no data        */
  537.         if (logRecordPtr != NULL && thisLogEntry != NULL) {
  538. #if GENERATINGPOWERPC
  539.             if (CompareAndSwap(0, 1, &LOG.semaphore) == FALSE) { /* Grab semaphore    */
  540.                 IncrementAtomic((SInt32 *) &LOG.lostLockCounter); /* Oops, it's busy*/
  541.                 status = fBsyErr;                                /* Inform caller    */
  542.             }
  543.             else {                                                /* Nope, we got it    */
  544. #else
  545.             {
  546.                 int                oldSR = __DisableInterrupts();
  547. #endif
  548.                 getIndex = LOG.entryGetIndex;
  549.                 if (getIndex != LOG.entryPutIndex) {            /* Not empty?        */
  550.                     status = noErr;
  551.                     if (++getIndex >= LOG.entryMaxIndex)
  552.                         getIndex = 0;
  553.                     *thisLogEntry = LOG.entries[getIndex];
  554.                     LOG.entryGetIndex = getIndex;
  555.                 }
  556. #if GENERATINGPOWERPC
  557.                 LOG.semaphore = 0;                                /* Free semaphore    */
  558. #else
  559.                 __EnableInterrupts(oldSR);
  560. #endif
  561.             }
  562.         }
  563.         return (status);
  564. }
  565.  
  566. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  567.  * CopyLogEntry
  568.  *
  569.  * Copy the specified LogRecord entry. This returns a copy of the entry, return TRUE
  570.  * if it was in range. This is only used by the DCMD to snapshot a LogRecord.
  571.  */
  572. pascal OSErr
  573. CopyLogEntry(
  574.         LogRecordPtr            logRecordPtr,            /* This log record            */
  575.         UInt32                    getIndex,                /* This entry index            */
  576.         LogEntryPtr                thisLogEntry            /* Store entry here            */
  577.     )
  578. {
  579.         OSErr                    status;
  580.  
  581.         status = readErr;
  582.         if (logRecordPtr != NULL && getIndex < LOG.entryMaxIndex) {
  583. #if GENERATINGPOWERPC
  584.             if (CompareAndSwap(0, 1, &LOG.semaphore) == FALSE) { /* Grab semaphore    */
  585.                 IncrementAtomic((SInt32 *) &LOG.lostLockCounter); /* Oops, it's busy*/
  586.                 status = fBsyErr;                                /* Inform caller    */
  587.             }
  588.             else {                                                /* Nope, we got it    */
  589. #else
  590.             {
  591.                 int                oldSR = __DisableInterrupts();
  592. #endif
  593.                 status = noErr;                                    /* Inform caller    */
  594.                 *thisLogEntry = LOG.entries[getIndex];            /* Store result        */
  595. #if GENERATINGPOWERPC
  596.                 LOG.semaphore = 0;                                /* Free semaphore    */
  597. #else
  598.                 __EnableInterrupts(oldSR);
  599. #endif
  600.             }
  601.         }
  602.         return (status);
  603. }
  604.  
  605. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  606.  * EnableLogRecord
  607.  *
  608.  * Enable/disable logging. Returns the old logging state.
  609.  */
  610. pascal Boolean32
  611. EnableLogRecord(
  612.         LogRecordPtr            logRecordPtr,            /* This log record            */
  613.         Boolean32                enableLogging
  614.     )
  615. {
  616.         return (
  617.             EnableLogCapability(logRecordPtr, enableLogging, kLogDataEnabledMask)
  618.         );
  619. }
  620.  
  621.  
  622. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  623.  * PreserveLogRecord
  624.  *
  625.  * Set the preserveFirst flag. Returns the old flag value. 
  626.  */
  627. pascal Boolean32
  628. PreserveLogRecord(
  629.         LogRecordPtr            logRecordPtr,            /* This log record            */
  630.         Boolean32                preserveFirst
  631.     )
  632. {
  633.         return (
  634.             EnableLogCapability(logRecordPtr, preserveFirst, kLogDataPreserveFirstMask)
  635.         );
  636. }
  637.  
  638. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  639.  * EnableLogUpTime
  640.  *
  641.  * Enable/disable recording of UpTime when WriteLogRecord. Returns the old state.
  642.  * UpTime generally would be left in its original, enabled, state. The state may be
  643.  * set FALSE to minimize the time needed to log data. If set FALSE, times will be
  644.  * recorded as zero.
  645.  */
  646. pascal Boolean32
  647. EnableLogUpTime(
  648.         LogRecordPtr            logRecordPtr,            /* This log record            */
  649.         Boolean32                enableUpTime            /* TRUE to enable UpTime    */
  650.     )
  651. {
  652.         return (
  653.             EnableLogCapability(logRecordPtr, enableUpTime, kLogDataRecordUpTimeMask)
  654.         );
  655. }
  656.  
  657. static Boolean32
  658. EnableLogCapability(
  659.         LogRecordPtr            logRecordPtr,            /* This log record            */
  660.         Boolean32                callerRequest,            /* TRUE to enable something    */
  661.         UInt32                    enableMaskBit            /* Selector bitmask            */
  662.     )
  663. {
  664.         Boolean32                result;
  665.         UInt32                    oldFlag;
  666.         UInt32                    newFlag;
  667.  
  668.         if (logRecordPtr == NULL)
  669.             oldFlag = 0;
  670.         else {
  671. #if GENERATINGPOWERPC
  672.             do {
  673.                 oldFlag = LOG.flags;
  674.                 if (callerRequest)
  675.                     newFlag = oldFlag | enableMaskBit;
  676.                 else {
  677.                     newFlag = oldFlag & ~enableMaskBit;
  678.                 }
  679.             } while (CompareAndSwap(oldFlag, newFlag, &LOG.flags) == FALSE);
  680. #else
  681.             int                    oldSR = __DisableInterrupts();
  682.  
  683.             oldFlag = LOG.flags;
  684.             if (callerRequest)
  685.                 newFlag = oldFlag | enableMaskBit;
  686.             else {
  687.                 newFlag = oldFlag & ~enableMaskBit;
  688.             }
  689.             __EnableInterrupts(oldSR);
  690. #endif
  691.         }
  692.         result = ((oldFlag & enableMaskBit) != 0);
  693.         return (result);
  694. }
  695.  
  696.  
  697. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  698.  * GetLogSemaphoreLostCounter
  699.  *
  700.  * Because the log library uses a "lossy" algorithm to maintain exclusive access to
  701.  * the critical section, there is a very slight possibility that two processes will
  702.  * try to access the log record at the same time. In this case, ReadLogEntry will
  703.  * stall (loop) and eventually succeed, while WriteLogEntry will lose the datum, and
  704.  * increment the logLostCounter.
  705.  */
  706. pascal UInt32
  707. GetLogSemaphoreLostCounter(
  708.         LogRecordPtr            logRecordPtr            /* This log record            */
  709.     )
  710. {
  711.         UInt32                    result;
  712.  
  713.         result = ((logRecordPtr == NULL) ? 0 : LOG.lostLockCounter);
  714.         return (result);
  715. }
  716.  
  717.  
  718. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  719.  * LogRecord utilities.
  720.  *
  721.  * These three functions are used to locate all registered log records. They may be
  722.  * used for a display utility or MacsBug dcmd. Applications call them as follows:
  723.  *
  724.  *    LogRecordIter            cookie;
  725.  *
  726.  *    if (LogRecordIterateCreate(&cookie) == noErr) {
  727.  *        while ((logRecordPtr = LogRecordIterate(&cookie)) != NULL) {
  728.  *            ... Process this LogRecord, the name may be extracted from the record ...
  729.  *        }
  730.  *        LogRecordIterateDispose(&cookie);
  731.  *    }
  732.  */
  733. pascal OSErr
  734. LogRecordIterateCreate(
  735.         LogRecordIterPtr        cookie
  736.     )
  737. {
  738.         OSErr                    status;
  739.  
  740.         cookie->logRecordPtr = GetFirstLogRecord();
  741.         status = noErr;
  742.         return (status);
  743. }        
  744.  
  745. pascal void
  746. LogRecordIterateDispose(
  747.         LogRecordIterPtr        cookie
  748.     )
  749. {
  750.         cookie->logRecordPtr = NULL;
  751.         /* Nothing to dispose */;
  752. }
  753.  
  754. pascal LogRecordPtr
  755. LogRecordIterate(
  756.         LogRecordIterPtr        cookie
  757.     )
  758. {
  759.         LogRecordPtr            logRecordPtr;
  760.  
  761.         logRecordPtr = cookie->logRecordPtr;
  762.         if (logRecordPtr != NULL)
  763.             cookie->logRecordPtr = LOG.logRecordLink;
  764.         return (logRecordPtr);
  765. }
  766.  
  767. #if GENERATINGPOWERPC
  768. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  769.  * LogUpTime
  770.  *
  771.  * This is the DriverServicesLibrary UpTime function, callable from 68000. It is
  772.  * called, once, by LogConvertTimestamp.
  773.  *
  774.  * Return values
  775.  *    resultPtr    AbsoluteTime
  776.  */
  777. pascal void
  778. LogUpTime(
  779.         AbsoluteTime            *resultPtr
  780.     )
  781. {
  782.         *resultPtr = UpTime();
  783. }
  784.  
  785. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  786.  * LogGetTimeBaseInfo
  787.  *
  788.  * This is the DriverServicesLibrary UpTime function, callable from 68000. It returns
  789.  * only those values that LogConvertTimestamp needs.
  790.  *
  791.  * Return values
  792.  *    upTimeNumerator        Needed for our AbsoluteToNanoseconds
  793.  *    upTimeDenominator    Needed for our AbsoluteToNanoseconds
  794.  */
  795. pascal void
  796. LogGetTimeBaseInfo(
  797.         UInt32                    *upTimeNumerator,
  798.         UInt32                    *upTimeDenominator
  799.     )
  800. {
  801.         UInt32                    unused;
  802.  
  803.         GetTimeBaseInfo(
  804.             &unused,                        /* minAbsoluteTimeDelta - unused        */
  805.             upTimeNumerator,                /* For AbsoluteTimeToNanoseconds        */
  806.             upTimeDenominator,                /* For AbsoluteTimeToNanoseconds        */
  807.             &unused,                        /* Unused                                */
  808.             &unused                            /* Unused                                */
  809.         );
  810. }
  811. #else
  812. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  813.  * LogUpTime
  814.  *
  815.  * This is the DriverServicesLibrary UpTime function, callable from 68000. It is
  816.  * called, once, by LogConvertTimestamp. Note: PowerPCClock trashes A3. The inline
  817.  * assembly is rather inefficient; but, of course, it is only called once.
  818.  *
  819.  * Return values
  820.  *    resultPtr    AbsoluteTime
  821.  */
  822. void PowerPCClock(
  823.         AbsoluteTime   *result
  824.     ) = {
  825.         0x205F,            /*    movea.l    (sp)+,a0            ; Fetch result address        */
  826.         0x48E7, 0x7F7E,    /*    movem.l    d1-d7/a1-a6,-(sp)    ; Save all registers        */
  827.         0x2F08,            /*    move.l    a0,-(sp)            ; Save result address        */
  828.         0xFE09,            /*    dc.w    0xFE09                ; Get UpTime from emulator    */
  829.         0x225F,            /*    movea.l    (sp)+,a1            ; Retrieve result address    */
  830.         0x2288,            /*    move.l    a0,(a1)                ; Store result low word        */
  831.         0x2340, 0x0004,    /*    move.l    d0,4(a1)            ; Store result high word    */
  832.         0x4CDF, 0x7EFE    /*    movem.l    (sp)+,d1-d7/a1-a6    ; Restore all registers        */
  833.     };
  834.  
  835. pascal void
  836. LogUpTime(
  837.         AbsoluteTime            *resultPtr
  838.     )
  839. {
  840.         PowerPCClock(resultPtr);
  841. }
  842.  
  843. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  844.  * LogGetTimeBaseInfo
  845.  *
  846.  * This is the DriverServicesLibrary UpTime function, callable from 68000. It returns
  847.  * only those values that LogConvertTimestamp needs.
  848.  *
  849.  * Return values
  850.  *    upTimeNumerator        Needed for our AbsoluteToNanoseconds
  851.  *    upTimeDenominator    Needed for our AbsoluteToNanoseconds
  852.  */
  853. pascal void
  854. LogGetTimeBaseInfo(
  855.         UInt32                    *upTimeNumerator,
  856.         UInt32                    *upTimeDenominator
  857.     )
  858. {
  859.         *upTimeNumerator = 4085;        /* These hard-wired values are only valid    */
  860.         *upTimeDenominator = 4096;        /* For the current "PowerSurge" machines    */
  861. }
  862.  
  863. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  864.  * GetFirstLogRecord
  865.  *
  866.  * The first time GetLogLibaryGlobal is called, it uses the Code Fragment Manager to
  867.  * locate _LogLibaryGlobal. If successful, it returns the contents of _LogLibraryGlobal.
  868.  * If it fails, it returns NULL.
  869.  */
  870. static LogRecordPtr
  871. GetFirstLogRecord(void)
  872. {
  873.         OSErr                    status;
  874.         LogRecordPtr            logRecordPtr;
  875.         THz                        currentHeap;
  876.         long                    gestaltResponse;
  877.         CFragConnectionID        connectionID;
  878.         CFragSymbolClass        symbolClass;
  879.         Ptr                        mainEntryAddr;
  880.         Str255                    errorName;
  881.         static LogRecordPtr        *gLogLibraryGlobalPtr;
  882.  
  883.         if (gLogLibraryGlobalPtr == NULL) {
  884.             status = Gestalt(gestaltCFMAttr, &gestaltResponse);
  885.             if (status == noErr
  886.              && (gestaltResponse & (1 << gestaltCFMPresent)) == 0)
  887.                 status = gestaltUndefSelectorErr;
  888.             if (status == noErr) {
  889.                 currentHeap = GetZone();
  890.                 SetZone(SystemZone());
  891.                 status = GetSharedLibrary(
  892.                             "\pLogLibrary",
  893.                             kPowerPCCFragArch,
  894.                             kLoadCFrag,
  895.                             &connectionID,
  896.                             &mainEntryAddr,
  897.                             errorName
  898.                         );
  899.                 SetZone(currentHeap);
  900.             }
  901.             if (status == noErr) {
  902.                 status = FindSymbol(
  903.                     connectionID,
  904.                     "\p_LogLibraryGlobal",
  905.                     (Ptr *) &gLogLibraryGlobalPtr,
  906.                     &symbolClass
  907.                 );
  908.             }
  909.             if (status != noErr)
  910.                 gLogLibraryGlobalPtr = (LogRecordPtr *) -1L;    /* failure        */
  911.         }
  912.         logRecordPtr = (gLogLibraryGlobalPtr != (LogRecordPtr *) -1L)
  913.                     ? *gLogLibraryGlobalPtr : NULL;
  914.         return (logRecordPtr);
  915. }
  916. #endif /* GENERATINGPOWERPC */
  917.  
  918. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  919.  * StringEquals
  920.  *
  921.  * Compare two strings, returning TRUE if equal. This eliminates dependency on
  922.  * the DriverServices Library.
  923.  */
  924. static Boolean
  925. StringEquals(
  926.         const char                *str1,
  927.         const char                *str2
  928.     )
  929. {
  930.         while (*str1 == *str2) {
  931.             if (*str1++ == 0)
  932.                 return (TRUE);
  933.             ++str2;
  934.         }
  935.         return (FALSE);
  936. }
  937.